﻿using Comsenz.Base;
using Comsenz.Discuz.Configs;
using Comsenz.Services.Blocks.Configs;
using Comsenz.Services.Blocks.Models;
using Lbb.Security;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Comsenz.Services.Blocks
{
    /// <summary>
    /// Discuz 模块生成器
    /// </summary>
    public abstract class BlockBuilderBase
    {
        #region Fields
        /// <summary>
        /// 最后一次处理时传入的参数。
        /// </summary>
        private Dictionary<string, object> Args;

        public readonly static DiscuzConfig Config = DiscuzConfig.Instance;

        #endregion Fields

        #region Props
        /// <summary>
        /// 最后一次生成结果是否成功。
        /// </summary>
        public bool Success { get; protected set; }

        #endregion Props

        /// <summary>
        /// 生成返回结果
        /// </summary>
        /// <param name="op">此参数表示客户端要请求数据</param>
        /// <param name="clientid">客户端ID（服务器分配给客户端的ID）</param>
        /// <param name="charset">客户端的数据编码</param>
        /// <param name="sign">数据签名，如果服务器端没有设置通信密钥则此值为空，如果服务器端不使用签名则此值为通信密钥；签名机制</param>
        /// <param name="start">用户指定显示的模块数据起始位置</param>
        /// <param name="items">用户指定显示的模块数据条数</param>
        /// <param name="bannedids">用户选择屏蔽某数据时记录在模块中的该数据 id，多个 id 以半角分号(,)分隔。 应该在获取数据时屏蔽该数据</param>
        /// <returns></returns>
        public virtual string Build(string op = null, string clientid = null, string charset = null, string sign = null, int? start = null, int? items = null, string bannedids = null)
        {
            Success = false;
            Args = InitArgs(ref op, ref clientid, ref charset, ref sign, ref start, ref items, ref bannedids);

            if (Config.CurrentConnectConfig.AppId != clientid) return "CLIENT_NOT_EXISTS";//客户端不存在

            if (GetSign(Args) != sign) return "SIGN_ERROR";//输入签名错误

            var result = string.Empty;
            try
            {
                switch ((op ?? "").ToLower())
                {
                    case "getconfig"://取得模块配置
                        result = XmlHelper.Serialize(new DefineData(GetConfig()).GetConfigValue(), true);
                        break;
                    case "getdata"://取得模块数据
                        result = XmlHelper.Serialize(GetData(Args).GetConfigValue(), true);
                        break;
                }
            }
            catch (Exception ex)
            {
#if DEBUG
                throw ex;
#endif
                return "SERVER_ERROR";//服务器异常。
            }
            Success = !string.IsNullOrEmpty(result);
            if (Success) return result;

            return "NO_OPERATION";//无效的操作请求。
        }

        /// <summary>
        /// 取得模块数据
        /// </summary>
        /// <param name="args">参数</param>
        /// <returns></returns>
        protected abstract ResultData GetData(Dictionary<string, object> args);

        /// <summary>
        /// 取得模块配置
        /// </summary>
        /// <returns></returns>
        protected abstract BlockConfig GetConfig();

        /// <summary>
        /// 初始化请求参数
        /// </summary>
        /// <param name="op">此参数表示客户端要请求数据</param>
        /// <param name="clientid">客户端ID（服务器分配给客户端的ID）</param>
        /// <param name="charset">客户端的数据编码</param>
        /// <param name="sign">数据签名，如果服务器端没有设置通信密钥则此值为空，如果服务器端不使用签名则此值为通信密钥；签名机制</param>
        /// <param name="start">用户指定显示的模块数据起始位置</param>
        /// <param name="items">用户指定显示的模块数据条数</param>
        /// <param name="bannedids">用户选择屏蔽某数据时记录在模块中的该数据 id，多个 id 以半角分号(,)分隔。 应该在获取数据时屏蔽该数据</param>
        protected virtual Dictionary<string, object> InitArgs(ref string op, ref string clientid, ref string charset, ref string sign, ref int? start, ref int? items, ref string bannedids)
        {
            var args = new Dictionary<string, object>();
            if (!(HttpContext.Current == null || HttpContext.Current.Request == null))
            {
                var Request = HttpContext.Current.Request;

                if (op == null) op = Request["op"];
                if (clientid == null) clientid = Request["clientid"];
                if (charset == null) charset = Request["charset"];
                if (sign == null) sign = Request["sign"];
                if (start == null)
                {
                    int t;
                    if (int.TryParse(Request["start"], out t) && t >= 0) start = t;
                }
                if (items == null)
                {
                    int t;
                    if (int.TryParse(Request["items"], out t) && t >= 1) items = t;
                }
                if (bannedids == null) bannedids = Request["bannedids"];

                if (op != null) args["op"] = op;
                if (clientid != null) args["clientid"] = clientid;
                if (charset != null) args["charset"] = charset;
                if (start != null) args["start"] = start;
                if (items != null) args["items"] = items;
                if (bannedids != null) args["bannedids"] = bannedids;

                foreach (var key in Request.Form.AllKeys)
                {
                    if ("sign" != key.ToLower() && !args.ContainsKey(key)) args[key] = Request[key];
                }
            }
            return args;
        }

        /// <summary>
        /// 生成签名
        /// </summary>
        /// <param name="para">参数数组</param>
        /// <param name="key">加密密钥</param>
        /// <returns></returns>
        public static string GetSign(IEnumerable<KeyValuePair<string, object>> para, string key = null)
        {
            string args;
            if (para == null || para.Count() == 0)
            {
                args = "";
            }
            else
            {
                para = para.OrderBy(d => d.Key);
                args = para.Select(d => string.Format("{0}={1}", d.Key, d.Value)).Aggregate((d, e) => d + "&" + e);
            }

            args += key ?? Config.CurrentConnectConfig.ConnectKey;
            return string.IsNullOrEmpty(args) ? "" : Cryptography.MD5(args);
        }

        public static int Time(DateTime? time = null, bool toUtcTime = true)
        {
            time = time.HasValue ?
                (toUtcTime ? time.Value.AddHours(-16) : time.Value) :
                DateTime.UtcNow;
            return (int)(time.Value - new DateTime(1970, 1, 1)).TotalSeconds;
        }

        protected static string[] ToStrings(string data, string se = ",")
        {
            return (data ?? "").Split((string.IsNullOrEmpty(se) ? "," : se).ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
        }

        protected static List<Guid> ToGuids(string ids, string se = ",")
        {
            return ToStrings(ids, se).Select(d =>
            {
                Guid id;
                return Guid.TryParse(d, out id) ? id : null as Guid?;
            }).Where(d => d != null).Cast<Guid>().ToList();
        }

        protected static List<int> ToInts(string ids, string se = ",")
        {
            return ToStrings(ids, se).Select(d =>
            {
                int id;
                return int.TryParse(d, out id) ? id : null as int?;
            }).Where(d => d != null).Cast<int>().ToList();
        }

        /// <summary>
        /// 取得参数值
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        protected string GetArgString(string key)
        {
            return Args.ContainsKey(key) ? (Args[key] ?? "").ToString() : null as string;
        }
        /// <summary>
        /// 取得参数值
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        protected string[] GetArgStrings(string key, string se = ",")
        {
            return ToStrings(GetArgString(key), se);
        }
        /// <summary>
        /// 取得参数值
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        protected bool? GetArgBool(string key)
        {
            bool bv;
            return Args.ContainsKey(key) && bool.TryParse((Args[key] ?? "").ToString(), out bv) ? bv : null as bool?;
        }

        /// <summary>
        /// 取得参数值
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        protected DateTime? GetArgDateTime(string key)
        {
            DateTime bv;
            return Args.ContainsKey(key) && DateTime.TryParse((Args[key] ?? "").ToString(), out bv) ? bv : null as DateTime?;
        }

        /// <summary>
        /// 取得参数值
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        protected int? GetArgInt(string key)
        {
            int iv;
            return Args.ContainsKey(key) && int.TryParse((Args[key] ?? "").ToString(), out iv) ? iv : null as int?;
        }
        /// <summary>
        /// 取得参数值
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        protected List<int> GetArgInts(string key)
        {
            return ToInts(GetArgString(key));
        }
        /// <summary>
        /// 取得参数值
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        protected decimal? GetArgDecimal(string key)
        {
            decimal dv;
            return Args.ContainsKey(key) && decimal.TryParse((Args[key] ?? "").ToString(), out dv) ? dv : null as decimal?;
        }
        /// <summary>
        /// 取得参数值
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        protected Guid? GetArgGuid(string key)
        {
            Guid gd;
            return Args.ContainsKey(key) && Guid.TryParse((Args[key] ?? "").ToString(), out gd) ? gd : null as Guid?;
        }
        /// <summary>
        /// 取得参数值
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        protected List<Guid> GetArgGuids(string key, string se = ",")
        {
            return ToGuids(GetArgString(key), se);
        }
    }
}
